[動画公開] RDSプロキシは未来を変えるか #devio2020
CX事業本部@大阪の岩田です。「Developers.IO 2020 CONNECT」 で 「RDSプロキシは未来を変えるか」 というタイトルで動画を公開しました。このブログではセッションの内容について簡単に紹介させて頂きます。
登壇動画
動画はこちらです。
約35分とそこそこのボリュームになっているので、35分も見てられない!という方向けに後ほど概要を説明していきます。
登壇資料
発表に使用した資料はこちらです。
以後内容について説明していきます。
大事なこと
RDS Proxyというテーマを選定したのが3ヶ月ほど前。動画公開までにはGAされるかなー?と淡い期待を頂きながら様子を見守りましたが、GAされませんでした。よって本セッションでは実際にRDS Proxyを利用して得られた知見をお話するような、いわゆる「やってみた」系の内容はありません。RDS ProxyがGAされた際に、正しく利用できるように、主にLambdaにおけるコネクションプーリング全般の考え方にフォーカスしてお話していきます。また、RDS Proxyの仕様についても一部お話していますが、実際にGAされた際は仕様が変更されている可能性がある点についてもご了承下さい。
2020年はサーバーレス元年?!
皆さんはこんな言葉を聞いたことが無いでしょうか?「2020年はサーバーレス元年」という言葉です。サーバーレスという言葉自体は数年前から流行している言葉ですが、実際のビジネスの現場では、まだまだ導入が進んでいるとは言い難い状況が続いていました。サーバーレスアーキテクチャの導入が進まない理由として、LambdaとRDSの相性問題を始めとしたサーバーレスなサービスの諸々の制約事項が挙げられるのですが、サービスのアップデートによりそういった制約事項が改善され、2020年はシステムのサーバーレス化が一気に加速するだろう といった趣旨の言葉です。
直近1年間のLambda関連のアップデートを振り返ると、多くのアップデートがありました。これらのアップデートを経て、Lambdaのユースケースは確実に広がってきました。そして、今注目されているのがRDS Proxyです。
そもそもコネクションプーリングとは?
RDS Proxyはコネクションプーリングを実現するためのマネージド・サービスです。まずコネクションプーリングという技術についておさらいしておきましょう。Googleで検索すると以下のような定義が見つかります。
コネクションプーリングとは、プログラムがデータベース管理システム(DBMS)へアクセスする際、アクセス要求のたびに接続や切断を繰り返すのではなく、一度形成した接続窓口(コネクション)を維持し続けて使い回す手法。
コネクションプーリングの実現方式としては、ざっくり以下の2パターンに別れます。
- アプリでプーリングするパターン
- ミドルウェアでプーリングするパターン
もう少し詳細を見ていきましょう。
アプリでプーリングするパターン
各プログラミング言語のライブラリやフレームワークがコネクションプールを管理する方式です。
ミドルウェアでプーリングするパターン
アプリケーションとDBの間にDBプロキシとして振る舞うミドルウェアを配置し、ミドルウェアがコネクションプールを管理する方式です。
アプリケーションサーバーのWorkerは通常のデータベースと同じようにデータベースプロキシに対して各種のリクエストを発行し、データベースプロキシがWorkerから受け付けたリクエストをバックエンドのデータベース接続に適切にルーティングします。
コネクションプーリングのメリット
コネクションプーリングにはどういったメリットがあるのでしょうか?大きく以下の2点と考えます。
- 接続のオーバーヘッド削減
- 論理的接続と物理的接続の分離
接続のオーバーヘッド削減
まず1つ目のメリットは接続のオーバーヘッド削減です。データベースとの接続確立処理は、いわゆる重い処理になります。TCPの3wayハンドシェイクやDBユーザーの認証認可、プロセスのFORKもしくはスレッドの生成といった処理が実行されます。アプリケーションからはコネクションプールの中にキャッシュされたOPEN済の接続を利用することで、DB接続のオーバーヘッドを削減することが期待できます。
論理的接続と物理的接続の分離
2つ目のメリットは論理的接続と物理的接続の分離です。アプリケーションから見たデータベース接続のライフサイクルを考えた場合、アプリケーションは常にデータベースにアクセスしているわけではありません。コネクションプールとデータベースの間の1つの物理的接続をアプリケーションとコネクションプール間の論理的な複数の接続に見せかけることで、DBの限られた資源を有効活用することができます。
論理的接続と物理的接続を分離することで、DBの最大同時接続数以上のリクエストが発生した場合でも、超過分のリクエストをWAITさせることでスパイク耐性の向上も見込めます。
Lambdaにおけるコネクションプーリング
どうもコネクションプーリングは良いことづくめに思えます。Lambdaからも利用したいところですが、「Lambdaはコネクションプーリングが使えない」という話をよく耳にします。この部分を掘り下げて考えていきます。
Lambdaのアーキテクチャ
Lambdaにおけるコネクションプーリングを考えるための前提条件として、Lambdaのアーキテクチャが重要になってきます。Lambdaのアーキテクチャは以下のような特徴を持ちます。
- Lambda実行環境はコンテナ内に構築される
- リクエストに応じてLambda実行環境を生成
- アイドル状態のLambda実行環境は破棄される
- 1つのLambda実行環境に対して同時にルーティングされるリクエストは1つだけ
このうち「1つのLambda実行環境に対して同時にルーティングされるリクエストは1つだけ」という特性が影響し、コネクションプーリングのメリットとしてあげた、論理的接続と物理的接続の分割というメリットを享受することができません。
Lambdaのプログラミングモデル
Lambdaの処理は初期化処理とhandler内の処理に別れます。初期化処理はLambd実行環境の構築を伴う、いわゆるコールドスタート時のみ実行される処理です。対してhandler内の処理は何かしらのイベントをトリガーにLambdaがInvokeされる都度実行される処理です。
初期化処理の中でDBに接続し、DB接続のオブジェクトをグローバル変数に代入しておくことで、handler内の処理ではOPEN済みのDB接続を扱うことが可能です。 いわゆる接続の永続化です。これをコネクションプーリングと呼ぶかは議論が分かれるところだと思いますが、接続のオーバーヘッドを削減するというアプローチはLambdaでも選択可能なチューニング手法です。しかし、この接続の永続化には1つ大きな問題があり、アプリケーション側から明示的にDB接続を閉じるタイミングが存在しないことに注意が必要です。
アイドル状態のLambdaがDB接続を食いつぶすというリスクがあるのです。
Lambdaはコネクションプーリングを活用し辛い
こういった事情から、Lambdaではコネクションプーリングを有効活用することが難しいです。ここまでがこれまでのLambdaとコネクションプーリングの関係性です。
RDS Proxy
このような課題のあったLambdaにおけるコネクションプーリングの問題ですが、re;invent2019にてRDS Proxyというサービスが発表されたことで状況が変化しました。このRDSプロキシというサービスはRDS向けにプロキシ型のコネクションプーリングを提供するマネージドサービスです。先程の分類した「ミドルウェアでプーリングするパターン」に該当するアーキテクチャです。これでLambdaからもコネクションプーリングが有効活用できる!!....のでしょうか?
RDS Proxyが向いているワークロード
- DBアクセスを伴わない処理がLambdaの中で多くの割合を占める場合
- トランザクションを利用しない処理
こういったワークロードではRDSプロキシが有効に働くでしょう。DBアクセスを伴わない処理を実行している間は、バックエンドのDB接続を別のLambda実行環境に割り当てても問題がないので、限られた物理接続を複数のLambda実行環境で共有することで、多くの処理を捌くことができるでしょう。
RDS Proxyが不向きなワークロード
逆にRDS Proxyが不向きと考えられるのは以下のようなワークロードです。
- DBアクセスを伴う処理が 多くの割合を占める場合
- ロングトランザクション
- リクエストが多すぎる場合
- 「ピン留め」を誘発するようなワークロード
RDS Proxyのピン留め
RDS Proxyを利用する上で、もっとも注意が必要と思われる概念です。クライアントからのリクエストによりDBセッション固有の情報が変更された場合、以後のリクエストは変更後の状態に依存するため、プールされた接続が特定のクライアントに固定され複数のクライアント間で共有できなくなる状況です。
このピン留めは様々な条件下で発生します。
- 一時テーブルを作成した場合
- プリペアドステートメント
- 拡張クエリプロトコルの使用(PostgreSQLの場合)
...etc
Postgresでは拡張クエリプロトコルがピン留め対象になっているとのことなので、何も意識せずにJava製のアプリからRDS Proxyに利用を試みた場合、ただパフォーマンスを悪化させるだけに終わるという最悪な結果を招き兼ねません。しっかりとピン留めの条件を把握し、ピン留めが発生しないもしくはし辛いようなアプリケーションの実装をこころがけましょう。
また、RDS Proxyの設定で「セッションのピン留めフィルター」を利用することで、特定のパラメータ変更についてはRDS Proxyのピン留め判定対象外とさせることが可能です。このあたりの設定値の調整がRDS Proxy利用にあたっての勘所になってくるでしょう。ぜひ一度公式ドキュメントをご確認下さい。
Amazon RDS Proxy (プレビュー) による接続の管理 ピン留めを回避する
RDS Proxy以外の選択肢
最後に、もう1つRDSプロキシを使わないという選択肢についても考えておきたいと思います。
まず、自身のユースケースに対してDBエンジンが適切なのか?ということを考えてみましょう。コネクションプーリングのメリットとして、接続のオーバーヘッド削減というメリットを上げましたが、高速な接続が要求されるのであれば、接続確立時にプロセスのForkが必要なPostgresよりはMySQLの方が向いているでしょう。なんとなく使い慣れているから という理由だけでDBエンジンを選定している場合は、一度見直してみるのも良いかもしれません。
次にRDBという選択肢は適切なのかを最高しましょう。コネクションプーリングはスケールアウトモデルとスケールアップモデルのギャップを緩和してくれますが、根本的にこの両者のアーキテクチャが変わるわけではありません。リクエスト数が多すぎる場合などは、DynamoDBのようなデータストアを選定する方が良いでしょう。
また、Lambdaは本当に適切なのか?ということも再考したい部分です。EC2やECSを使って、アプリケーション側のコネクションプーリングを利用したほうが、コネクションプーリングによるメリットを享受しやすいかもしれません。
まとめ
これまで説明してきたように、RDS Proxyを利用することで、LambdaとRDSの相性の悪さを緩和することができます。しかし、緩和はあくまで緩和であって、スケーラビリティに関するLambdaとRDSの根本的なモデルの違いは埋まっていません。RDS Proxyがあれば、何も気にせずにRDSを使っても大丈夫…と考えるのではなく、まずはDynamoDBで要件を満たせないか?というところから検討すべきでしょう。
検討の結果、業務要件として、データストアにRDBが必須の場合はRDS Proxyの利用をセットで検討することになりますが、RDS Proxyが有効に機能するかはワークロードに依存しています。思考停止的にRDS Proxyを導入するのではなく、しっかりと事前分析と検証を行いましょう。場合によっては、プリペアドステートメントの利用をやめるなど、アプリケーションの改修とセットで導入を検討しましょう。